// ***************************************************************************************
// Copyright (c) 2023-2025 Peng Cheng Laboratory
// Copyright (c) 2023-2025 Institute of Computing Technology, Chinese Academy of Sciences
// Copyright (c) 2023-2025 Beijing Institute of Open Source Chip
//
// iEDA is licensed under Mulan PSL v2.
// You can use this software according to the terms and conditions of the Mulan PSL v2.
// You may obtain a copy of Mulan PSL v2 at:
// http://license.coscl.org.cn/MulanPSL2
//
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
// EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
// MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
//
// See the Mulan PSL v2 for more details.
// ***************************************************************************************
/**
 * @File Name: contest_guide.cpp
 * @Brief :
 * @Author : Yell (12112088@qq.com)
 * @Version : 1.0
 * @Creat Date : 2023-09-15
 *
 */
#pragma once
#include <set>
#include <string>
#include <vector>
#include<cfloat>

#include "contest_coord.h"
#include "Point.hh"
#include "api/TimingEngine.hh"

namespace ieda_contest {

enum class ContestPIN_TYPE
{
  kNoPinType,
  kPrimaryInput,
  kPrimaryOutput,
  kPrimaryInputOutput,
  kInstancePort,
  kInstanceInput,
  kInstanceOutput,
  kInstanceInputOutput,
  kFlipFlopClk,
  kSteiner,
  kFakePin,
  kFakeSinkPin,
  kFakeDriverPin

};
enum class ContestPIN_FALG_TYPE
{
  kNoPinFlagType,
  kClockPin,
  kResetPin
};

class ContestInstance;
class ContestNet;
// class ContestDB;

class ContestPin
{
 public:
  ContestPin() = default;
  explicit ContestPin(std::string name);
  ~ContestPin() = default;
  // getter
  std::string get_name() const { return _name; }              
  ContestCoord& get_real_coord() { return _real_coord; }
  ContestCoord& get_grid_coord() { return _grid_coord; }
  std::vector<std::string>& get_contained_instance_list() { return _contained_instance_list; }
  std::vector<std::string>& get_contained_pin_list() { return _contained_pin_list; }
  
  ContestInstance* get_ContestInstance() const { return _ContestInstance; } 
  ContestNet* get_ContestNet() const { return _ContestNet; }
                 
  Point<int32_t> get_center_coord() const { return _center_coord; }
  Point<int32_t> get_offset_center_coord() const { return _offset_coord; }
  //待处理
  double get_early_slack() const;
  double get_late_slack() const;
  double get_arrival_early_time() const;
  double get_arrival_late_time() const;
  double get_required_early_time() const;
  double get_required_late_time() const;
  float get_k_i() const { return _k_i;}
  double get_lateWeight() const { return _late_weight; }
  double get_earlyWeight() const { return _early_weight; }
  int get_centrality()  const { return _centrality; }


  bool isNoPinType()  { return _pin_type == ContestPIN_TYPE::kNoPinType; }
  bool isFlipFlopClkPin() { return _pin_type == ContestPIN_TYPE::kFlipFlopClk; }
  bool isPO() { return _pin_type == ContestPIN_TYPE::kPrimaryOutput; }
  bool isPI() { return _pin_type == ContestPIN_TYPE::kPrimaryInput; }
  bool isPIO(){return _pin_type == ContestPIN_TYPE::kPrimaryInputOutput; }
  bool isInstanceInputPin() { return _pin_type == ContestPIN_TYPE::kInstanceInput; }
  bool isInstanceOutputPin() { return _pin_type == ContestPIN_TYPE::kInstanceOutput; }
  bool isClockPin() { return _pin_flag_type == ContestPIN_FALG_TYPE::kClockPin; }
  bool isResetPin() { return _pin_flag_type == ContestPIN_FALG_TYPE::kResetPin; }
  bool isNoFlagTypePin() { return _pin_flag_type == ContestPIN_FALG_TYPE::kNoPinFlagType;  }

  // setter
  void set_real_coord(const ContestCoord& real_coord) { _real_coord = real_coord; }
  void set_grid_coord(const ContestCoord& grid_coord) { _grid_coord = grid_coord; }
  void set_contained_instance_list(const std::vector<std::string>& contained_instance_list)
  {
    _contained_instance_list = contained_instance_list;
  }
  void set_contained_pin_list(const std::vector<std::string>& contained_pin_list) { _contained_pin_list = contained_pin_list; }

  void set_pin_type(ContestPIN_TYPE pin_type) { _pin_type = pin_type; }
  void set_pin_flag_type(ContestPIN_FALG_TYPE pin_flag_type) { _pin_flag_type = pin_flag_type; }
  void set_coord(Point<int32_t> coord) { _center_coord = coord; }
  void set_offset_coord(Point<int32_t> coord) { _offset_coord = coord; }
  void set_ContestInstance(ContestInstance* ContestInst) { _ContestInstance = ContestInst; }
  void set_ContestNet(ContestNet* ContestNet) { _ContestNet = ContestNet; }
  void set_k_i(float ki)  { _k_i = ki; }
  void set_lateWeight(double late_weight){ _late_weight = late_weight; }
  void set_earlyWeight(double early_weight){ _early_weight = early_weight; }
  void set_centrality(int centrality) {  _centrality = centrality; }
  // static void set_db(ContestDB* db) {_contest_db = db;}

 private:
  // static ContestDB* _contest_db;
  ContestCoord _real_coord;
  ContestCoord _grid_coord;
  std::vector<std::string> _contained_instance_list;
  std::vector<std::string> _contained_pin_list;
  std::string _name;
  ContestPIN_TYPE _pin_type;
  ContestPIN_FALG_TYPE _pin_flag_type;

  ContestInstance* _ContestInstance;
  ContestNet* _ContestNet;

  Point<int32_t> _offset_coord;
  Point<int32_t> _center_coord;
  float _k_i;
  double _early_weight;
  double _late_weight;
  int _centrality;
};

inline ContestPin::ContestPin(std::string name) : _name(name),_pin_type(ContestPIN_TYPE::kNoPinType),_pin_flag_type(ContestPIN_FALG_TYPE::kNoPinFlagType), _ContestInstance(nullptr), _ContestNet(nullptr){}

inline double ContestPin::get_early_slack() const
{
  double early_slack = 0;
  ista::TimingEngine* timing_engine = ista::TimingEngine::getOrCreateTimingEngine();
  auto rise_value = timing_engine->reportSlack(_name.c_str(), ista::AnalysisMode::kMin, ista::TransType::kRise);
  auto fall_value = timing_engine->reportSlack(_name.c_str(), ista::AnalysisMode::kMin, ista::TransType::kFall);

  if (rise_value == std::nullopt || fall_value == std::nullopt) {
    return DBL_MAX;
  }

  early_slack = std::min(rise_value.value(), fall_value.value());

  return early_slack;
}

inline double ContestPin::get_late_slack() const
{
  double late_slack = 0;
  ista::TimingEngine* timing_engine = ista::TimingEngine::getOrCreateTimingEngine();

  auto rise_value = timing_engine->reportSlack(_name.c_str(), ista::AnalysisMode::kMax, ista::TransType::kRise);
  auto fall_value = timing_engine->reportSlack(_name.c_str(), ista::AnalysisMode::kMax, ista::TransType::kFall);

  if (rise_value == std::nullopt || fall_value == std::nullopt) {
    return DBL_MAX;
  }

  late_slack = std::min(rise_value.value(), fall_value.value());

  return late_slack;
}

inline double ContestPin::get_arrival_early_time() const
{
  double arrival_early_time = 0;
  ista::TimingEngine* timing_engine = ista::TimingEngine::getOrCreateTimingEngine();

  auto rise_value = timing_engine->reportAT(_name.c_str(), ista::AnalysisMode::kMin, ista::TransType::kRise);
  auto fall_value = timing_engine->reportAT(_name.c_str(), ista::AnalysisMode::kMin, ista::TransType::kFall);

  if (rise_value == std::nullopt || fall_value == std::nullopt) {
    return DBL_MIN;
  }

  arrival_early_time = std::min(rise_value.value(), fall_value.value());

  return arrival_early_time;
}

inline double ContestPin::get_arrival_late_time() const
{
  double arrival_late_time = 0;
  ista::TimingEngine* timing_engine = ista::TimingEngine::getOrCreateTimingEngine();

  auto rise_value = timing_engine->reportAT(_name.c_str(), ista::AnalysisMode::kMax, ista::TransType::kRise);
  auto fall_value = timing_engine->reportAT(_name.c_str(), ista::AnalysisMode::kMax, ista::TransType::kFall);

  if (rise_value == std::nullopt || fall_value == std::nullopt) {
    return DBL_MIN;
  }

  arrival_late_time = std::max(rise_value.value(), fall_value.value());

  return arrival_late_time;
}

inline double ContestPin::get_required_early_time() const
{
  double required_early_time = 0;
  ista::TimingEngine* timing_engine = ista::TimingEngine::getOrCreateTimingEngine();

  auto rise_value = timing_engine->reportRT(_name.c_str(), ista::AnalysisMode::kMin, ista::TransType::kRise);
  auto fall_value = timing_engine->reportRT(_name.c_str(), ista::AnalysisMode::kMin, ista::TransType::kFall);

  if (rise_value == std::nullopt || fall_value == std::nullopt) {
    return DBL_MAX;
  }

  required_early_time = std::max(rise_value.value(), fall_value.value());

  return required_early_time;
}

inline double ContestPin::get_required_late_time() const
{
  double required_late_time = 0;
  ista::TimingEngine* timing_engine = ista::TimingEngine::getOrCreateTimingEngine();

  auto rise_value = timing_engine->reportRT(_name.c_str(), ista::AnalysisMode::kMax, ista::TransType::kRise);
  auto fall_value = timing_engine->reportRT(_name.c_str(), ista::AnalysisMode::kMax, ista::TransType::kFall);

  if (rise_value == std::nullopt || fall_value == std::nullopt) {
    return DBL_MAX;
  }

  required_late_time = std::min(rise_value.value(), fall_value.value());

  return required_late_time;
}



}  // namespace ieda_contest
